package org.sanguine.study.hometasks.second;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Reader;
import java.util.PriorityQueue;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.Scanner;


import org.sanguine.study.hometasks.first.Runner;
import org.sanguine.study.hometasks.first.TaskWorker;
import org.sanguine.study.hometasks.second.auxiliary.Cell;

public class Task16 extends Runner implements TaskWorker {
	
	protected PrintWriter out;
	protected Reader in;
	private Integer[] center;
	private Integer radius;
	private PriorityQueue<Cell> cells = new PriorityQueue<>();
	
	public static void main(String[] args) {
		Runner runner = new Task16();
		runner.run(args);
	}
	
	@Override
	protected TaskWorker getWorkerInstance() {
		return this;
	}

	protected void printUsage() {
		System.err.println("USAGE: java -jar Task16.jar 'output_filename'");
	}

	@Override
	public boolean setUp(Reader in, PrintWriter out, String[] args) {
		if (args == null || args.length < 1) {
			printUsage();
			return false;
		}
		
		if (in == null || out == null) return false;
		
		out.print("Input center of circle by cells (x, y): ");
		Scanner scanner = new Scanner(in);
		String inputString = scanner.nextLine();
		center = parseIntegerPair(inputString);
		if (center == null) {
			System.err.println("Wrong coordinates of center");
			scanner.close();
			return false;
		}
		
		out.print("Input radius (by cells): ");
		inputString = scanner.nextLine();
		scanner.close();
		
		if (inputString != null && !inputString.isEmpty()) {
			try {
				radius = Integer.parseInt(inputString);
			} catch (NumberFormatException e) {
				System.err.println("Wrong number format");
				return false;
			}
		} else {
			System.err.println("Radius must be set");
			return false;
		}
		
		this.in = in;	
		try {
			this.out = new PrintWriter(new FileWriter(args[0]), true);
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}

	private Integer[] parseIntegerPair(String inputString) {
		Integer[] pair = null;
		
		if (inputString != null && !inputString.isEmpty()) {
			Pattern pattern = Pattern.compile("(\\S+),\\s*(\\S+)");
			Matcher matcher = pattern.matcher(inputString);
			while (matcher.find()) {
				String xString = matcher.group(1);
				String yString = matcher.group(2);
				pair = new Integer[2];
				try {
					pair[0] = Integer.parseInt(xString);
					pair[1] = Integer.parseInt(yString);
				} catch (NumberFormatException e) {
					return null;
				}
			}
		}
		return pair;
	}

	@Override
	public void doTask() {
		if (center == null || radius == null || out == null) return;
		findCells();
		printCells();
	}

	private void printCells() {
		out.println("Cells within given circle:");
		for (Cell cell = cells.poll(); cell != null; cell = cells.poll())
			out.println(cell);
	}
	
	/**
	 * Find all cells within the boundaries of the circle.
	 * Based on Bresenham's circle algorithm
	 * 
	 */

	private void findCells() {
		cells.clear();
		int x = 0;
		int y = radius;
		int d = 3 - 2 * y;
		while (x <= y) {
			addCellsFromColumns(x, y, center[0], center[1], radius);
			if (d < 0) d += 4 * x + 6;
			else {
				d += 4 * (x - y) + 10;
				y--;
			}
			x++;
		}
	}

	private void addCellsFromColumns(int x, int y, int xc, int yc, int r) {
		addCellsBetweenVerticalBoundaries(x + xc, -y + yc, y + yc, xc, yc);
		if (x != 0) addCellsBetweenVerticalBoundaries(-x + xc, -y + yc, y + yc, xc, yc);
		if (y <  r) {
			addCellsBetweenVerticalBoundaries(y + xc, -x + yc, x + yc, xc, yc);
			addCellsBetweenVerticalBoundaries(-y + xc, -x + yc, x + yc, xc, yc);
		}
	}

	private void addCellsBetweenVerticalBoundaries(int column, int from, int to, int xc, int yc) {
		for (int i = from + 1; i < to; i++)
			cells.add(new Cell(column, i, xc, yc));
	}
}
